package com.happyfamily.springboot.crowdsourcingplatform.security;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import com.happyfamily.springboot.crowdsourcingplatform.security.authentication.*;
import com.happyfamily.springboot.crowdsourcingplatform.verificationcode.CaptchaException;
import com.happyfamily.springboot.crowdsourcingplatform.verificationcode.LoginAuthenticationFailureHandler;
import com.happyfamily.springboot.crowdsourcingplatform.verificationcode.LoginAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.*;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * @Author Administrator
 * @create 2020/02/16 17:18
 */
@EnableWebSecurity
@Configuration
public class SpringSecurityConfigFront
{
	/**
	 * 前端端安全框架
	 */
	@Configuration
	@Order(1)
	public static class FontConfigurationAdapter extends WebSecurityConfigurerAdapter{



		@Autowired
		private UserDetailsService userDetailsService;

		@Autowired
		private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

		@Autowired
		private MyAuthenctiationFailureHandler myAuthenctiationFailureHandler;

		@Autowired
		private RestAuthenticationAccessDeniedHandler restAuthenticationAccessDeniedHandler;

		@Autowired
		private MyLogoutSuccessHandler myLogoutSuccessHandler;

		@Autowired
		private MySessionExpiredStrategy sessionExpiredStrategy;


		@Override
		protected void configure(AuthenticationManagerBuilder auth) throws Exception
		{
			auth.userDetailsService(userDetailsService).passwordEncoder(passwordFrontEncoder());
		}


		@Override
		protected void configure(HttpSecurity http) throws Exception
		{
			http.csrf().disable();
			http

					.antMatcher("/api-front/**")
					.authorizeRequests()//启用基于 HttpServletRequest 的访问限制，开始配置哪些URL需要被保护、哪些不需要被保护
					.antMatchers().permitAll()//未登陆用户允许的请求
					.anyRequest()
					.authenticated();



			//解决X-Frame-Options DENY问题
			http.headers().frameOptions().sameOrigin();
			http.
					formLogin()
					.loginPage("/user-login")//登陆界面页面跳转URL
					.loginProcessingUrl("/api-front/login")//登陆界面发起登陆请求的URL
					.successHandler(myAuthenticationSuccessHandler)
					.failureHandler(myAuthenctiationFailureHandler)
			.and()
					.logout()
					.logoutSuccessUrl("/frontLogout")
					.invalidateHttpSession(true)//指定是否在退出时httpsession无效，默认为true
					.deleteCookies("JSESSIONID")//删除cookies
					.logoutSuccessHandler(myLogoutSuccessHandler)//退出成功时的动作
			 .and()
			         .sessionManagement()//添加session管理器
			        .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)//如果有需要就创建一个Session(默认)登录时
			.and()
					.addFilterBefore(loginAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
					.exceptionHandling()
					.authenticationEntryPoint(loginUrlAuthenticationEntryPoint());  //使用新的登录EntryPoint
//					.invalidSessionUrl("/session/invalid") // Session失效后跳转到这个链接
//					.maximumSessions(1)//最大Session并发数量为1个
//					.maxSessionsPreventsLogin(true)//当Session达到最大有效数的时候，不再允许相同的账户登录
//					.expiredSessionStrategy(sessionExpiredStrategy)

			//异常处理
			http.exceptionHandling().accessDeniedHandler(restAuthenticationAccessDeniedHandler);

		}


		/**
		 * 处理密码加密解密逻辑
		 *
		 * @return
		 */
		@Bean
		public PasswordEncoder passwordFrontEncoder()
		{
			return new BCryptPasswordEncoder();
		}

		/*声明登录endpoint Bean*/
		@Bean
		public LoginUrlAuthenticationEntryPoint loginUrlAuthenticationEntryPoint(){
			LoginUrlAuthenticationEntryPoint loginUrlAuthenticationEntryPoint=new LoginUrlAuthenticationEntryPoint("/user-login");
			return loginUrlAuthenticationEntryPoint;
		}



		/*声明验证码生成策略属性 Bean*/
		@Bean
		public DefaultKaptcha captchaProducer(){
			DefaultKaptcha captchaProducer =new DefaultKaptcha();
			Properties properties =new Properties();
			properties.setProperty("kaptcha.border","yes");
			properties.setProperty("kaptcha.border.color","105,179,90");
			properties.setProperty("kaptcha.textproducer.font.color","red");
			properties.setProperty("kaptcha.image.width","125");
			properties.setProperty("kaptcha.image.height","45");
			properties.setProperty("kaptcha.textproducer.font.size","45");
			properties.setProperty("kaptcha.session.key","code");
			properties.setProperty("kaptcha.textproducer.char.length","4");
			properties.setProperty("kaptcha.textproducer.font.names","宋体,楷体,微软雅黑");
			Config config=new Config(properties);
			captchaProducer.setConfig(config);
			return  captchaProducer;
		}

		/*声明LoginAuthenticationFilter Bean*/
		@Bean
		public LoginAuthenticationFilter loginAuthenticationFilter() throws Exception {
			LoginAuthenticationFilter loginAuthenticationFilter=new LoginAuthenticationFilter();
			loginAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());
			loginAuthenticationFilter.setFilterProcessesUrl("/authentication");
			loginAuthenticationFilter.setAuthenticationSuccessHandler(new SavedRequestAwareAuthenticationSuccessHandler());
			loginAuthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
			return loginAuthenticationFilter;
		}


		/*声明授权失败异常处理 Bean*/
		@Bean
		public AuthenticationFailureHandler authenticationFailureHandler() {
			//根据抛出的不同的异常跳转到不同的URL
			ExceptionMappingAuthenticationFailureHandler failureHandler = new ExceptionMappingAuthenticationFailureHandler();
			Map<String, String> failureUrlMap = new HashMap<>();
			failureUrlMap.put(BadCredentialsException.class.getName(), LoginAuthenticationFailureHandler.PASS_ERROR_URL);
			failureUrlMap.put(CaptchaException.class.getName(), LoginAuthenticationFailureHandler.CODE_ERROR_URL);
			failureUrlMap.put(AccountExpiredException.class.getName(), LoginAuthenticationFailureHandler.EXPIRED_URL);
			failureUrlMap.put(LockedException.class.getName(), LoginAuthenticationFailureHandler.LOCKED_URL);
			failureUrlMap.put(DisabledException.class.getName(), LoginAuthenticationFailureHandler.DISABLED_URL);
			failureHandler.setExceptionMappings(failureUrlMap);
			return failureHandler;
		}

	}


}
